import { baseParaTagName } from '../config';
import { simplify, normalize, clearEmptyNode } from '../clear';
import { findPara, findBlock } from '../find';
import { getParaListFromNodeOrFrag, getTextNodesFromRange } from '../get';
import { createElementPath } from '../createElement';
import { selectBlock, normalizeRange } from '../range';
import { ensureSelectContent, ensureNewLine } from '../assert';
import { addLangSelectListener, traceCode } from '../init/initLangSelect';
import { fillStyle } from '../fillStyle';
import hljs from '../highlight/index';

import { record } from '../revoke/record';

let root;

const clear = () => {
  simplify(root);
  normalize(root);
}
let selection, range;

function syncHeight() {
  window.parent.postMessage(JSON.stringify({
    type: 'changeRTFHeight',
    height: root.scrollHeight
  }))
}

function setRoot(a){
  if(typeof a === 'string') root = document.querySelector(a);
  else if(a instanceof Node) root = a;
  else {
    console.error(`message handler set root: root-${root} is not a string or node`);
  }
}

function addBlock(tag) {
  let newNodes = createElementPath(tag.name), newNode = newNodes[0];
  let startPara = findPara(range.startContainer);
  let endPara = findPara(range.endContainer);

  let startBlock = findBlock(range.startContainer, root);
  let endBlock = findBlock(range.endContainer, root);
  if(startBlock === endBlock && startBlock !== root) {
    alert('不能嵌套');
    return;
  }
  let frag = range.extractContents();

  if(startPara === endPara) {
    range.setEndAfter(endPara); 
    // split a paragraph to insert a blockTag
    range.insertNode(range.extractContents());

    // insert new node
    newNodes[newNodes.length - 1].appendChild(frag);
  } else {
    let paraList = getParaListFromNodeOrFrag(frag);
    let paraTagName = newNodes[newNodes.length - 1].tagName;
    paraList.forEach((para, idx) => {
      if(para.tagName !== paraTagName) {
        let paraRange = new Range();
        paraRange.selectNodeContents(paraList[idx]);
        let paraContent = paraRange.extractContents();
        paraList[idx] = document.createElement(paraTagName);
        paraList[idx].appendChild(paraContent);
      }
    })
    paraList.forEach(para => {
      newNode.appendChild(para);
    });
  }

  if(newNode.childNodes.length > 1) newNode.removeChild(newNode.firstChild);
  range.insertNode(newNode);

  if(newNode.tagName === 'CODE') {
    addLangSelectListener(newNode);
  }

  selectBlock(range, newNode);

  clearEmptyNode(newNode.previousSibling);
  clearEmptyNode(newNode.nextSibling);
}

function addImg(tag) {
  let newNodes = createElementPath(tag.name), newNode = newNodes[0];
  let startPara = findPara(range.startContainer);
  let endPara = findPara(range.endContainer);

  let img = document.createElement('img');
  img.src = tag.url;
  img.style.width = '80%';
  img.style.verticalAlign = 'bottom';
  let newPara = document.createElement(baseParaTagName);
  newPara.style.textAlign = 'center';
  newPara.appendChild(img);
  img.addEventListener('load', () => {
    syncHeight();
  })
  if(startPara === endPara) {
    range.extractContents();
    range.setEndAfter(startPara);
    range.insertNode(range.extractContents());
  } else {
    range.extractContents();
  }
  startPara.parentNode.insertBefore(newPara, startPara.nextSibling);

  selectBlock(range, newPara);
}

function addLink(tag) {
  let newNodes = createElementPath(tag.name), newNode = newNodes[0];
  let startPara = findPara(range.startContainer);
  let endPara = findPara(range.endContainer);
  
  ensureSelectContent(range);

  range.extractContents();

  ensureSelectContent(range);

  let a = document.createElement('a');
  a.href = tag.href;
  a.appendChild(document.createTextNode(tag.cover));

  a.addEventListener('click', () => {
    window.open(a.href);
  })

  range.insertNode(a);
}

function addInline(tag) {
  let textRanges = getTextNodesFromRange(range);

  textRanges.forEach(range => {
    let frag = range.extractContents();
    let pack = document.createElement(tag.name);
    if(tag.style) fillStyle(pack, tag.style);
    pack.appendChild(frag);
    range.insertNode(pack);
  });

  let head = textRanges[0], tail = textRanges[textRanges.length - 1];
  range.setStart(head.startContainer, head.startOffset);
  range.setEnd(tail.endContainer, tail.endOffset);
}

function addTag(tag) {
  selection = window.getSelection();
  if(!selection.rangeCount) return;
  range = selection.getRangeAt(0);

  record();
  normalizeRange(range);

  if(tag.name === 'img') {
    addImg(tag);
    return;
  }

  if(tag.name === 'a') {
    addLink(tag);
    return;
  }

  if(tag.type === 'block') {
    addBlock(tag);
  } 
  else if(tag.type === 'inline') {
    addInline(tag);
  }
}

function messageHandler(event) {
  try{
    JSON.parse(event.data);
  } catch {
    return;
  }
  let operation = JSON.parse(event.data);
  if(operation.type === 'addTag') {
    addTag(operation.tag);
    syncHeight();
    ensureNewLine(root);
  }
  else if(operation.type === 'getArticle') {
    clear();
    let html = '';
    root.childNodes.forEach(node => {
      if(!(node instanceof Node)) return;
      if(node.tagName === 'CODE') {
        let code = node;
        let paras = code.getElementsByTagName('p');
        let string = '';
        for(let j = 0; j < paras.length; j++) {
          let para = paras.item(j);
          string = string.concat(para.innerText);
          string += '\n';
        }
        string = hljs.highlight(string, {
          language: code.dataset.lang
        }).value;
        html += `<pre><code data-lang="${code.dataset.lang}">${string}</code></pre>`;
      } else {
        html += node.outerHTML;
      }
    })
    window.parent.postMessage(
      JSON.stringify({content: html, type: 'sendArticle', text: root.innerText}),
    );
  }
  else if(operation.type === 'getRawArticle') {
    clear();
    window.parent.postMessage(
      JSON.stringify({content: root.innerHTML, type: 'sendRawArticle', text: root.innerText}),
    );
  }
  else if(operation.type === 'fillDraft') {
    root.innerHTML = operation.draft;
    console.log('fill draft', operation.draft);
    traceCode(root);
    syncHeight();
  } else if(operation.type === 'clear') {
    root.innerHTML = '<p></p>';
  }
}

export {
  setRoot,
  messageHandler
};